import * as http from 'http'
import * as ws from 'ws'
import { LogAll, ClientInfo, BaseInfo, LogRuntime } from './interfaces'
import _ from 'lodash'
import { ERRORS, UUID } from './utils/common'
import { getLoginUser } from './utils/users'
import { log_after_id } from './actions'
import { doEncode, getUserToken } from './global'

/** sse专用 */
const writeInfo = (resp: http.ServerResponse, info: any, token: string) => {
  resp?.write(`data:${JSON.stringify(doEncode(info, token))}\n\n`)
}
/** ws专用 */
const sendInfo = (ws: ws.WebSocket, info: any, token: string) => {
  ws.send(JSON.stringify(doEncode(info, token)))
}

/**
 * Map<username, Record<username, ClientInfo>>
 */
const clients_map = new Map<string, ClientInfo[]>()


/** 统一消息长链接 */
export const listen$sse = async function (req?: http.IncomingMessage, resp?: http.ServerResponse) {
    if (!req || !resp) return
    const loginUser = await getLoginUser(req)
    if (!loginUser || !req['data']) {
        writeInfo(resp, {
          type: 'server_error',
          message: {
            ts: Date.now(),
            error: ERRORS.NOT_LOGIN,
          }
        }, null)
        resp.end()
        return
    }
    const { _id } = req['data']
    const uuid = UUID()
    const { username } = loginUser
    const clients = clients_map.get(username) || []
    clients.push({
        uuid,
        username, req, resp,
        gmt_create: Date.now(),
        gmt_update: Date.now(),
    })
    clients_map.set(username, clients)
    req.on('close', function () {
        const clients = clients_map.get(username) || []
        clients_map.set(username, clients.filter(c => c.uuid === uuid))
        resp?.end()
    })
    sendLogMessageTo([username], 'connect', clients.map(({req, resp, ws, ...client}) => {
      return client
    }))

    if (_id) {
      const result = await log_after_id({ _id }, { req, loginUser })
      if (result?.data?.length) {
        for (let i = 0; i < result.data.length; i++) {
          writeInfo(resp, result.data[i], loginUser.token)
        }
      }
    }
}

/** 服务推送消息 */
export const sendLogMessageTo = function <T extends keyof LogAll> (...args: T extends LogRuntime ? [
    to: string[],
    type: T,
    message: LogAll[T],
  ] : [
    to: string[],
    type: T,
    message: LogAll[T],
    baseInfo: Required<BaseInfo>,
    chatroom_id: BaseInfo['_id'],
  ]
) {
  const [ to, type, message, baseInfo, chatroom_id ] = args
  const info = { type, message, chatroom_id, ...(baseInfo || {}) }
  for (let i = 0; i < to.length; i++) {
        const username = to[i];
        const token = getUserToken(username)
        const clients = clients_map.get(username) || []
        clients.forEach((client) => {
            writeInfo(client.resp, info, token)
        })

        const wss = wss_map.get(username) || []
        wss.forEach(client => {
          sendInfo(client.ws, info, token)
        })
    }
}


/**
 * Map<username, Record<username, ClientInfo>>
 */
const wss_map = new Map<string, ClientInfo[]>()

export const onServerCreate = function (server: any) {
  const wss = new ws.Server({server})
  wss.on('connection', async function (ws, req) {
    const loginUser = await getLoginUser(req)
    if (!loginUser) {
        sendInfo(ws, {
          type: 'server_error',
          message: {
            ts: Date.now(),
            error: ERRORS.NOT_LOGIN,
          }
        }, null)
        ws.close()
        return
    }

    const uuid = UUID()
    const { username, token } = loginUser
    const clients = wss_map.get(username) || []
    clients.push({
      uuid,
      username, req, ws,
      gmt_create: Date.now(),
      gmt_update: Date.now(),
    })
    wss_map.set(username, clients)

    ws.on('close', function () {
      const clients = wss_map.get(username) || []
      wss_map.set(username, clients.filter(c => c.uuid === uuid))
    })

    // url模块相关API修改, 使用URL
    const url = new URL('http://localhost' + req.url)
    const params = url.searchParams
    const _id = params.get('_id')

    sendLogMessageTo([username], 'connect', clients.map(({req, resp, ws, ...client}) => {
      return client
    }))

    if (_id) {
      const result = await log_after_id({ _id }, { req, loginUser })
      if (result?.data?.length) {
        for (let i = 0; i < result.data.length; i++) {
          sendInfo(ws, result.data[i], token)
        }
      }
    }
  })
}
